import { GlCard, GlIcon, GlBadge, GlSprintf, GlLink } from '@gitlab/ui';
import { nextTick } from 'vue';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import ExplainVulnerability from 'ee/vulnerabilities/components/explain_vulnerability/explain_vulnerability.vue';
import ExplainVulnerabilityPrompt from 'ee/vulnerabilities/components/explain_vulnerability/explain_vulnerability_prompt.vue';
import ExplainVulnerabilityDrawer from 'ee/vulnerabilities/components/explain_vulnerability/explain_vulnerability_drawer.vue';

const MOCK_VULNERABILITY = { id: 1 };

describe('Explain Vulnerability component', () => {
  let wrapper;

  const createWrapper = ({ stubs } = {}) => {
    wrapper = shallowMountExtended(ExplainVulnerability, {
      propsData: { vulnerability: MOCK_VULNERABILITY },
      stubs,
    });
  };

  const findTryButton = () => wrapper.findByTestId('try-button');
  const findVulnerabilityPrompt = () => wrapper.findComponent(ExplainVulnerabilityPrompt);
  const findVulnerabilityDrawer = () => wrapper.findComponent(ExplainVulnerabilityDrawer);

  const clickTryButton = () => {
    findTryButton().vm.$emit('click');
    return nextTick();
  };

  beforeEach(createWrapper);

  describe('GlCard component', () => {
    it('shows the GlCard with the expected title', () => {
      expect(wrapper.findComponent(GlCard).text()).toContain(ExplainVulnerability.i18n.cardTitle);
    });

    it('shows the tanuki-ai icon', () => {
      expect(wrapper.findComponent(GlIcon).props('name')).toBe('tanuki-ai');
    });

    it('shows the maturity badge with expected link', () => {
      const badge = wrapper.findComponent(GlBadge);

      expect(badge.text()).toBe('Beta');
      expect(badge.attributes('href')).toBe(ExplainVulnerability.maturityDocHref);
      expect(badge.props()).toMatchObject({
        variant: 'neutral',
        size: 'sm',
      });
    });

    it('shows the card body text', () => {
      expect(wrapper.findComponent(GlSprintf).attributes('message')).toBe(
        ExplainVulnerability.i18n.cardText,
      );
    });

    it('shows the link to the feedback issue', () => {
      createWrapper({ stubs: { GlSprintf } });

      expect(wrapper.findComponent(GlLink).attributes('href')).toBe(
        ExplainVulnerability.feedbackIssueHref,
      );
    });

    it('shows the Try it out button', () => {
      expect(findTryButton().text()).toBe('Try it out');
      expect(findTryButton().props()).toMatchObject({
        variant: 'confirm',
        icon: 'tanuki-ai',
      });
    });
  });

  describe('Try it out button', () => {
    it('is enabled when the drawer is closed', () => {
      expect(findTryButton().props('disabled')).toBe(false);
    });

    it('is disabled when the drawer is open', async () => {
      await clickTryButton();

      expect(findTryButton().props('disabled')).toBe(true);
    });

    it.each`
      phrase           | isLoading
      ${'loading'}     | ${true}
      ${'not loading'} | ${false}
    `('is $phrase when the prompt loading state is $isLoading', async ({ isLoading }) => {
      createWrapper();
      findVulnerabilityPrompt().vm.$emit('loading-state-changed', isLoading);
      await nextTick();

      expect(findTryButton().props('loading')).toBe(isLoading);
    });
  });

  describe('Explain vulnerability drawer', () => {
    it('is closed by default', () => {
      expect(findVulnerabilityDrawer().props('isOpen')).toBe(false);
    });

    it('is opened when the Try it out button is clicked', async () => {
      await clickTryButton();

      expect(findVulnerabilityDrawer().props('isOpen')).toBe(true);
    });

    it.each([true, false])(
      'has the expected props for includeSourceCode: %s',
      async (includeSourceCode) => {
        findVulnerabilityPrompt().vm.$emit('checkbox-changed', includeSourceCode);
        await nextTick();

        expect(findVulnerabilityDrawer().props()).toMatchObject({
          includeSourceCode,
          vulnerability: MOCK_VULNERABILITY,
        });
      },
    );

    it('closes the drawer when the close event is triggered', async () => {
      await clickTryButton();
      // Safety check to verify that the drawer is open before closing it.
      expect(findVulnerabilityDrawer().props('isOpen')).toBe(true);

      findVulnerabilityDrawer().vm.$emit('close');
      await nextTick();

      expect(findVulnerabilityDrawer().props('isOpen')).toBe(false);
    });
  });

  describe('Vulnerability prompt', () => {
    it('shows the vulnerability prompt', () => {
      createWrapper();

      expect(findVulnerabilityPrompt().props()).toMatchObject({
        vulnerabilityGraphqlId: 'gid://gitlab/Vulnerability/1',
        isDrawerOpen: expect.any(Boolean),
      });
    });
  });
});
